package com.vehicle.common.redis;

import com.vehicle.common.redis.config.RedisNode;
import com.vehicle.common.redis.connection.RedisConnectionFactory;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPoolConfig;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by yantingbin on 2017/7/4.
 */
@Configuration
@ConditionalOnClass({Jedis.class})
public class RedisAutoConfiguration {
    @Bean(name="com.vehicle.common.redis.RedisProperties")
    @ConditionalOnMissingBean
    public RedisProperties redisProperties(){
        return new RedisProperties();
    }

    @Bean(name="com.vehicle.common.redis.RedisProperties.Pool")
    @ConditionalOnMissingBean
    public RedisProperties.Pool redisPoolProperties(){
        return new RedisProperties.Pool();
    }

    @Bean(name="com.vehicle.common.redis.RedisProperties.Cluster")
    @ConditionalOnMissingBean
    public RedisProperties.Cluster redisClusterProperties(){
        return new RedisProperties.Cluster();
    }

    /**
     * Base class for Redis configurations
     */
    protected abstract class AbstractRedisConfiguration{
        @Autowired
        protected RedisProperties properties;

        protected RedisConnectionFactory applyProperties(RedisConnectionFactory factory){
            if(properties.getPassword()!=null){
                factory.setPassword(properties.getPassword());
            }

            RedisProperties.Cluster clusterProperties=this.properties.getCluster();

            factory.setCluster("cluster".equals(clusterProperties.getClusterModel()));

            if(clusterProperties.getNodes()!=null){
                factory.setRedisServers(createRedisServers(clusterProperties));
            }
           return factory;
        }
        private List<RedisNode> createRedisServers(RedisProperties.Cluster cluster){
           List<RedisNode> redisServers=new ArrayList<>();
           String nodes=cluster.getNodes();
           for(String node: StringUtils.commaDelimitedListToStringArray(nodes)){
               try{
                    String[] parts=StringUtils.split(node,":");
                   Assert.state(parts.length==2,"Must be defined as 'host:port'");
                   redisServers.add(new RedisNode(parts[0],Integer.valueOf(parts[1])));
               }catch (RuntimeException ex){
                  throw  new IllegalStateException("Invalid redis cluster "+"property '"+node+"'",ex);
               }
           }
           return redisServers;
        }
    }

    @Configuration
    @ConditionalOnClass(GenericObjectPool.class)
    protected class RedisPooledConnectionConfiguration extends AbstractRedisConfiguration{
     @Bean
     @ConditionalOnMissingBean(RedisConnectionFactory.class)
      public RedisConnectionFactory redisConnectionFactory(){
          return applyProperties(createConnectionFactory());
      }
      private RedisConnectionFactory createConnectionFactory(){
          if(this.properties.getPool()!=null){
              return new RedisConnectionFactory(jedisPoolConfig());
          }
          return new RedisConnectionFactory();
      }
      private JedisPoolConfig jedisPoolConfig(){
          JedisPoolConfig config=new JedisPoolConfig();
          RedisProperties.Pool props=this.properties.getPool();
          config.setMaxTotal(props.getMaxActive());
          config.setMaxIdle(props.getMaxIdle());
          config.setMinIdle(props.getMinIdle());
          config.setMaxWaitMillis(props.getMaxWait());
          return config;
      }
    }

}
